package service;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @Description
 *              <p>
 *              问题：一个文件中有10000个数，用Java实现一个多线程程序将这个10000个数输出到5个不用文件中（
 *              不要求输出到每个文件中的数量相同）。
 *              要求启动10个线程，两两一组，分为5组。每组两个线程分别将文件中的奇数和偶数输出到该组对应的一个文件中，
 *              需要偶数线程每打印10个偶数以后，
 *              就将奇数线程打印10个奇数，如此交替进行。同时需要记录输出进度，每完成1000个数就在控制台中打印当前完成数量，
 *              并在所有线程结束后，在控制台打印”Done”.
 * @Date 2016年7月18日 上午9:26:05
 * @Version V1.0
 */
public class Print1W {

	public final static int totalCount = 10000;
	private final static int THREAD_NUMBER = 10;
	private final static CountDownLatch countDownLatch = new CountDownLatch(THREAD_NUMBER);

	public static void main(String[] args) throws IOException, InterruptedException {

		String filePath = "F:/test/source_data.txt";
		String outFilePrefix = "F:/test/targe_data_";
		BufferedWriter bw = new BufferedWriter(new FileWriter(new File(filePath)));
		Random r = new Random();
		int range = totalCount * 10;
		for (int i = 0; i < totalCount; i++) {
			bw.write(r.nextInt(range) + "\n");
		}
		bw.close();

		ExecutorService es = Executors.newFixedThreadPool(THREAD_NUMBER);
		BufferedReader br = new BufferedReader(new FileReader(new File(filePath)));
		LinkedBlockingQueue<Integer> odds = new LinkedBlockingQueue<Integer>(totalCount);
		LinkedBlockingQueue<Integer> evens = new LinkedBlockingQueue<Integer>(totalCount);
		try {
			String str;
			while (((str = br.readLine()) != null) && !str.isEmpty()) {
				Integer num = Integer.valueOf(str);
				if (num % 2 == 0) { // 放入偶数队列
					evens.add(num);
				} else { // 奇数
					odds.add(num);
				}
			}

			AtomicInteger total = new AtomicInteger();
			for (int i = 1; i <= 5; i++) {
				File outFile = new File(outFilePrefix + i + ".txt");
				Object lock = new Object(); // 组线程互斥锁对象
				AtomicInteger guard = new AtomicInteger(1); // 组任务线程轮流执行哨兵
				AtomicInteger complete = new AtomicInteger(0);
				es.submit(new PrintWorker(complete, lock, guard, false, countDownLatch, total, odds, outFile));
				es.submit(new PrintWorker(complete, lock, guard, true, countDownLatch, total, evens, outFile));
			}
		} finally {
			br.close();
		}

		es.shutdown();
		countDownLatch.await();
		System.out.println("Done!");
	}

}

class PrintWorker implements Runnable {

	private final AtomicInteger compelete;
	private final Object lock;
	private final AtomicInteger guard;
	private boolean evenTask = false;
	private final CountDownLatch countDownLatch;
	private final AtomicInteger total;
	private final LinkedBlockingQueue<Integer> numbers;
	private File outFile;

	private volatile int printNum = 0;

	public PrintWorker(AtomicInteger compelete, Object lock, AtomicInteger guard, boolean evenTask, CountDownLatch countDownLatch, AtomicInteger total,
			LinkedBlockingQueue<Integer> numbers, File outFile) {
		this.compelete = compelete;
		this.lock = lock;
		this.guard = guard;
		this.evenTask = evenTask;
		this.countDownLatch = countDownLatch;
		this.total = total;
		this.numbers = numbers;
		this.outFile = outFile;
	}

	public void run() {

		while (true) {

			if (total.get() == Print1W.totalCount) { // 1w个数全部输出完毕，退出任务
				System.out.println(Thread.currentThread().getName() + " exit for all done.");
				break;
			}
			if (numbers.isEmpty()) { // 队列为空，退出任务
				System.out.println(Thread.currentThread().getName() + " exit for empty queue. current total complete:" + total.get());
				break;
			}

			synchronized (lock) { // 同组的两个任务线程互斥
				int currentGuard = guard.get();
				boolean evenGuard = (currentGuard % 2 == 0);
				/**
				 * 判断是否轮流到本任务线程执行条件：奇数-奇数哨兵 || 偶数-偶数哨兵||
				 * 同一组的另一个任务线程执行完毕退出，本任务线程不再等待
				 */
				if ((evenTask && evenGuard) || (!evenTask && !evenGuard) || compelete.get() > 0) {

					FileWriter fw = null;
					try {
						fw = new FileWriter(outFile, true);
						/**
						 * 退出条件：数据队列被取空 || 写入10条数据 || 所有数据均写入完成
						 */
						while (!(printNum == 10)) {
							Integer d = numbers.poll();
							if (d != null) {

								fw.append(String.valueOf(d) + "\n");

								++printNum;
								int totalCount = total.incrementAndGet();

								if (totalCount % 1000 == 0) {
									System.out.println("current completed count is " + totalCount);
								}

								if (totalCount == Print1W.totalCount) {
									break;
								}
							} else {
								break;
							}
						}

						// 数据被取完， 队列为空，交给另一个奇/偶任务
						resetAndExchange();
					} catch (IOException e) {
						e.printStackTrace();
					} finally {
						try {
							if (fw != null) {
								fw.close();
							}
						} catch (IOException e) {
							e.printStackTrace();
						}
					}
				}
			}
		}

		// 跳出while循环，该任务线程执行完成
		/**
		 * 通知和该组的另一个线程，我的任务完成了，不用等我打印，你可以继续随便玩了
		 */
		compelete.incrementAndGet();
		// 倒数计数-1
		countDownLatch.countDown();
	}

	private void resetAndExchange() {
		guard.incrementAndGet();
		printNum = 0;
	}
}